package cn.ly.rabbit.config;

import org.springframework.amqp.core.*;
import org.springframework.amqp.rabbit.config.SimpleRabbitListenerContainerFactory;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.amqp.rabbit.listener.RabbitListenerContainerFactory;
import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.HashMap;
import java.util.Map;

@Configuration
public class RabbitConfig {

    public static final String Simple_Queue_Name = "hello.queue";
    public static final String Email_Queue_Name = "myEmail.queue";
    public static final String User_Queue_Name = "myUser.queue";

    //目标队列：其设置了死信信息，如果失败将转发给死信交换器
    public static final String B_Queue_Name = "b.queue";
    //死信队列1：Topic类型的死信交换器
    public static final String DLX_Queue_Name = "myDlx.queue";
    //死信队列2：Direct类型的死信交换器
    public static final String Delay_Queue_Name = "myDelay.queue";
    public static final String Order_Queue_Name = "myOrder.queue";       //生产者发送到该队列上，但我们不设置监听器，不去消费它，那么它消息过期后就会转发到死信交换器上


    public static final String Direct_Exchange_Name = "myDirect.exchange";
    public static final String Topic_Exchange_Name = "myTopic.exchange";
    //死信交换器1：Topic类型
    public static final String DLX_Exchange_Name = "myDlx.exchange";
    //死信交换器2：Direct类型
    public static final String DLX_Delay_Exchange_Name = "myDlx.direct.exchange";




    //最简单的队列，其交换器默认为Direct交换器，绑定建默认为queue_name
    @Bean
    public Queue helloQueue() {
        return new Queue(Simple_Queue_Name);
    }

    //Email队列
    @Bean
    public Queue queueEmailMessage() {
        return new Queue(Email_Queue_Name);
    }

    //用户队列
    @Bean
    public Queue queueUserMessages() {
        return new Queue(User_Queue_Name);
    }

    //声明个Direct交换器
    @Bean
    public DirectExchange directExchange() {
        return new DirectExchange(Direct_Exchange_Name);
    }

    //声明个Topic交换器
    @Bean
    public TopicExchange topicExchange() {
        return new TopicExchange(Topic_Exchange_Name);
    }


    //绑定Email队列到Direct交换器上
    @Bean
    public Binding bindingEmailExchangeMessage() {
        return BindingBuilder
                .bind(queueEmailMessage())
                .to(directExchange())
                .with("sb.ll.email");   //绑定键
    }

    //将User队列绑定到Topic交换器上
    @Bean
    public Binding bindingUserExchangeMessage() {
        return BindingBuilder
                .bind(queueUserMessages())
                .to(topicExchange())
                .with("li.*.user");    //绑定键
    }



    //====================================================
    //死信交换器1（注意此设置为Topic类型的死信交换器。其实如果只有一个死信监听者，那么声明为fanout交换器更好）
    @Bean
    public TopicExchange exchangeDLX() {
        return new TopicExchange(DLX_Exchange_Name);
    }

    //死信对列1
    @Bean
    public Queue queueDLX() {
        return new Queue(DLX_Queue_Name);
    }

    //绑定死信对列1 到 死信交换器上
    @Bean
    public Binding bindingExchangeDLX() {
        return BindingBuilder
                .bind(queueDLX())
                .to(exchangeDLX())
                .with("dlx.test.#");    //绑定键
    }

    //生产者：需要发送的的目标对列
    @Bean
    public Queue queueHelloB() {
        Map<String, Object> args = new HashMap<>();
        //声明当该队列上的消息成为死信时，要转发到该死信交换器中：
        args.put("x-dead-letter-exchange", DLX_Exchange_Name);
        //声明死信路由键!!!!!!!!!!!!!!!!  一旦指定后就不能随意更改了。否则该队列持久化到RabbitMq中，重启项目会报错。如果你要再改这个路由键，只能在rabbitmq中先删除掉该队列，在重启项目
        args.put("x-dead-letter-routing-key", "dlx.test.B");  //【死信路由键】上面将死信路由器，与【死信队列1】绑定键设置为了：dlx.test.#
        //声明队列消息过期时间 5000ms
//        args.put("x-message-ttl", 5000);       //我们一般不会设置队列的过期时间，而是设置消息的过期时间，颗粒度更细一些
        return new Queue(B_Queue_Name, true, false, false, args);
    }

    //将B队列绑定到Topic交换器上
    @Bean
    public Binding bindingBExchangeMessage() {
        return BindingBuilder
                .bind(queueHelloB())
                .to(topicExchange())
                .with("test.b.*");    //绑定键
    }


    //====================================================
    //死信交换器2（Direct死信交换器）
    @Bean
    public DirectExchange exchangeDLX2() {
        return new DirectExchange(DLX_Delay_Exchange_Name);
    }
    //死信对列
    @Bean
    public Queue queueDLXDelay() {
        return new Queue(Delay_Queue_Name);
    }
    //绑定死信对列 到 死信交换器上
    @Bean
    public Binding bindingExchangeDLXDelay() {
        return BindingBuilder
                .bind(queueDLXDelay())
                .to(exchangeDLX2())
                .with(Delay_Queue_Name);    //绑定键，此处懒得起名，直接用死信队列的名字了
    }


    //目标队列【延时订单业务】
    @Bean
    public Queue queueDLXOrder() {
        Map<String, Object> args = new HashMap<>();
        //声明当该队列上的消息成为死信时，要转发到该死信交换器中：
        args.put("x-dead-letter-exchange", DLX_Delay_Exchange_Name);
        //声明死信路由键!!!!!!!!!!!!!!!!  一旦指定后就不能随意更改了。否则该队列持久化到RabbitMq中，重启项目会报错。如果你要再改这个路由键，只能在rabbitmq中先删除掉该队列，在重启项目
        args.put("x-dead-letter-routing-key", Delay_Queue_Name);  //【死信路由键】上面将死信路由器，与死信队列绑定键设置为了：Delay_Queue_Name
        return new Queue(Order_Queue_Name, true, false, false, args);
    }

    //绑定目标对列 到 死信交换器上【处理延时订单业务】
    @Bean
    public Binding bindingExchangeDLXOrder() {
        return BindingBuilder
                .bind(queueDLXOrder())
                .to(exchangeDLX2())
                .with(Order_Queue_Name);    //绑定键
    }

    //++++++++++++++++++++++++++Direct死信交换器_End





    //==========================消息转换器：将复杂对象使用jackson反序列化
    // 【需要你生产者中的实体类的包名.类名  与你消费者项目对应的该实体类：包名.类名一致，才能反序列化】比较垃圾
    @Bean
    public RabbitListenerContainerFactory<?> rabbitListenerContainerFactory(ConnectionFactory connectionFactory) {
        SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();
        factory.setConnectionFactory(connectionFactory);
        factory.setMessageConverter(new Jackson2JsonMessageConverter());
        return factory;
    }

}
